package models

import (
	"crypto/md5"
	"fmt"
	"gitchina/kly_service/data"
	"gitchina/kly_service/errors"
	"gitchina/kly_service/template"
	"gitchina/kly_service/util"
	"gitchina/kly_service/util/sms"
	"math/rand"
	"strconv"
	"strings"
	"time"
)

type MakeWaybillRQ struct {
	Id                ID      `json:"waybill_id"`
	StartArea         int     `json:"start_area"`
	StartAddr         string  `json:"start_addr"`
	EndArea           int     `json:"end_area"`
	EndAddr           string  `json:"end_addr"`
	SrcGeo            string  `json:"src_geo"`
	DestGeo           string  `json:"dest_geo"`
	SrcLocationTitle  string  `json:"src_location_title"`
	DestLocationTitle string  `json:"dest_location_title"`
	SrcLocation       string  `json:"src_location"`
	DestLocation      string  `json:"dest_location"`
	ShipmentTime      string  `json:"shipment_time"`
	CarrierPubId      ID      `json:"carrier_pub_id"`
	CarrierOwnerId    int     `json:"-"`
	CargoName         string  `json:"cargo_name"`
	CargoType         int     `json:"cargo_type"`
	CargoBulk         float32 `json:"cargo_bulk"`
	CargoWeight       float32 `json:"cargo_weight"`
	CargoCount        int     `json:"cargo_count"`
	CargoValue        float32 `json:"cargo_value"`
	ArrivalTime       string  `json:"arrival_time"`
	Deposit           float32 `json:"deposit"`
	Price             float32 `json:"price"`
	Consignor         string  `json:"consignor"`
	Consignee         string  `json:"consignee"`
	ConsigneePhone    string  `json:"consignee_phone"`
	Remark            string  `json:"remark"`
	NeedInvoice       bool    `json:"need_invoice"`
	NeedInsurance     bool    `json:"need_insurance"`
	NeedOilCard       bool    `json:"need_oilcard"`
	OilCardNo         string  `json:"oilcard_no"`
	PaymentTerm       int     `json:"payment_term"`
	LogisticCorp      string
	ContractType      int
	State             int
	TaxRatio          float32
	PriceIncludeTax   float32
}

type MakeWaybillRS struct {
	WaybillId   ID          `json:"waybill_id"`
	PayAmountRS PayAmountRS `json:"pay_amount"`
}

type GetWaybillListRQ struct {
	QueryState string `json:"query_state"`
	PageNo     int    `json:"page_no"`
	PageSize   int    `json:"page_size"`
}

type GetWaybillDetailRQ struct {
	Id ID `json:"waybill_id"`
}

type WaybillListRS struct {
	TotalCount int           `json:"total_count"`
	List       []WaybillList `json:"waybill_list"`
}

type WaybillList struct {
	Id           ID      `json:"waybill_id"`
	ShipmentTime string  `json:"shipment_time"`
	StartArea    int     `json:"start_area"`
	EndArea      int     `json:"end_area"`
	Deposit      float32 `json:"deposit"`
	Price        float32 `json:"price"`
	CargoType    int     `json:"cargo_type"`
	CargoName    string  `json:"cargo_name"`
	PaymentTerm  int     `json:"payment_term"`
	State        int     `json:"state"`
}

type WaybillDetailRS struct {
	CargoOwner        string      `json:"cargo_owner"`
	CargoOwnerPhone   string      `json:"cargo_owner_phone"`
	CargoOwnerId      ID          `json:"cargo_owner_id"`
	CarrierOwner      string      `json:"carrier_owner"`
	CarrierOwnerPhone string      `json:"carrier_owner_phone"`
	CarrierOwnerId    ID          `json:"carrier_owner_id"`
	StartArea         int         `json:"start_area"`
	StartAddr         string      `json:"start_addr"`
	EndArea           int         `json:"end_area"`
	EndAddr           string      `json:"end_addr"`
	SrcGeo            string      `json:"src_geo"`
	DestGeo           string      `json:"dest_geo"`
	SrcLocationTitle  string      `json:"src_location_title"`
	DestLocationTitle string      `json:"dest_location_title"`
	SrcLocation       string      `json:"src_location"`
	DestLocation      string      `json:"dest_location"`
	VehicleNo         string      `json:"vehicle_no"`
	VehicleType       int         `json:"vehicle_type"`
	VehcileLen        int         `json:"vehicle_len"`
	VehicleLoad       int         `json:"vehicle_load"`
	ShipmentTime      string      `json:"shipment_time"`
	ArrivalTime       string      `json:"arrival_time"`
	CarrierPubId      ID          `json:"carrier_pub_id"`
	CargoName         string      `json:"cargo_name"`
	CargoType         int         `json:"cargo_type"`
	CargoBulk         float32     `json:"cargo_bulk"`
	CargoWeight       float32     `json:"cargo_weight"`
	CargoCount        int         `json:"cargo_count"`
	CargoValue        float32     `json:"cargo_value"`
	Deposit           float32     `json:"deposit"`
	Price             float32     `json:"price"`
	Consignor         string      `json:"consignor"`
	Consignee         string      `json:"consignee"`
	ConsigneePhone    string      `json:"consignee_phone"`
	Remark            string      `json:"remark"`
	State             int         `json:"waybill_state"`
	NeedInvoice       bool        `json:"need_invoice"`
	NeedInsurance     bool        `json:"need_insurance"`
	NeedOilCard       bool        `json:"need_oil_card"`
	PaymentTerm       int         `json:"payment_term"`
	OilCardNo         string      `json:"oil_card_no"`
	LineType          int         `json:"line_type"`
	InvoiceInfo       InvoiceInfo `json:"invoice_info"`
}

type UpdateWaybillStateWithParamsRQ struct {
	Id            ID              `json:"waybill_id"`
	UpdateParam   MakeWaybillRQ   `json:"waybill_update_param"`
	NeedInvoice   bool            `json:"need_invoice"`
	NeedInsurance bool            `json:"need_insurance"`
	PaymentTerm   int             `json:"payment_term"`
	InvoiceCorpId int             `json:"invoice_corp_id"`
	InvoiceInfo   InvoicePostAddr `json:"invoice_info"`
	State         WaybillState    `json:"waybill_state"`
}

type UpdateOfflineWaybillStateWithParamsRQ struct {
	Id               ID              `json:"waybill_id"`
	UpdateParam      MakeWaybillRQ   `json:"waybill_update_param"`
	BankName         string          `json:"bank_name"`
	PayeeName        string          `json:"payee"`
	BankCard         string          `json:"bank_card"`
	PayeeIDCard      string          `json:"payee_idcard"`
	InvoiceIssueCorp string          `json:"invoice_issue_corp"`
	NeedInsurance    bool            `json:"need_insurance"`
	InvoiceInfo      InvoicePostAddr `json:"invoice_info"`
	ContractType     int
}

type UpdateOilCardRQ struct {
	WaybillId   ID     `json:"waybill_id"`
	NeedOilCard bool   `json:"need_oilcard"`
	OilCardNo   string `json:"oilcard_no"`
}

type Contract struct {
	CargoOwnerName    string
	CargoCorpName     string
	InvoiceCorpName   string
	CargoOwnerPhone   string
	CargoCorpPhone    string
	VehicleNo         string
	CarrierOwnerName  string
	CarrierOwnerPhone string
	CarrierOwnerAddr  string
	DrivingLicneseNo  string
	StartArea         int
	EndArea           int
	CargoName         string
	CargoType         int
	CargoCount        int
	CargoValue        float32
	Price             float32
	Weight            float32
	Bulk              float32
	Remark            string
	Consignor         string
	Consignee         string
	ConsigneePhone    string
	CargoRcvUnitName  string
	CargoRcvAddr      string
	ArrivalTime       string
	PaymentTerm       int
	IsCorp            bool
	NeedOilCard       bool
}

type WaybillMessage struct {
	Text       string `json:"text"`
	CreateTime string `json:"create_time"`
}

type AddWaybillScoreRQ struct {
	WaybillID ID     `json:"waybill_id"`
	Score     int    `json:"score"`
	Comment   string `json:"comment"`
}

type WaybillScore struct {
	NickName   string `json:"commenter_name"`
	Score      int    `json:"score"`
	Comment    string `json:"comment"`
	CreateTime string `json:"create_time"`
}

type GetWaybillScoreRQ struct {
	CarrierOwnerID ID `json:"carrier_owner_id"`
}

type WaybillState int

const (
	O_Wait2PayDeposit WaybillState = iota
	O_Wait2Confirm
	O_OnTheWay
	O_OnContract_Wait2Pay
	O_OnContract_Wait2Confirm
	O_OnShipment
	O_OnArrival
	O_Complete
	O_Cancel = 10
)

func init() {
	go autoComplete()
}

func (o Order) MakeWaybill(mbrId ID, rq *MakeWaybillRQ) (rs MakeWaybillRS, err error) {
	var mId, pId, id int64
	var walletDto data.WalletDto
	var mbrRepo data.MemberRepo
	var orderRepo data.OrderRepo
	if _, _, _, mId, err = mbrId.Decode(); err != nil {
		err = errors.New(errors.MSG_Mbr_Invalid)
		return
	}

	if _, _, _, pId, err = rq.CarrierPubId.Decode(); err != nil {
		err = errors.New(errors.MSG_Logi_Invalid_Pub_Id)
		return
	}

	if !mbrRepo.EnsureCargoOwner(int(mId)) {
		err = errors.New(errors.MSG_Mbr_Only_Cargo_Owner_Can_Make_Waybill)
		return
	}

	// price = 0 线下协商后更新运费
	// if rq.Price <= 0 {
	// 	err = errors.New(errors.MSG_Order_Price_Lt_Zero)
	// 	return
	// }

	if err = o.checkValidWaybillRQ(int(pId), rq); err == nil {
		var payRepo data.PayRepo
		var state int
		if walletDto, err = payRepo.GetWallet(int(mId)); err == nil {
			if rq.Deposit == 0 {
				state = 1
			}

			id, err = orderRepo.MakeWaybill(&data.WaybillDto{
				CargoOwnerId:      int(mId),
				CarrierOwnerId:    rq.CarrierOwnerId,
				CarrierPubId:      int(pId),
				ShipmentTime:      rq.ShipmentTime,
				StartArea:         rq.StartArea,
				StartAddr:         rq.StartAddr,
				EndArea:           rq.EndArea,
				EndAddr:           rq.EndAddr,
				SrcGeo:            rq.SrcGeo,
				DestGeo:           rq.DestGeo,
				SrcLocationTitle:  rq.SrcLocationTitle,
				DestLocationTitle: rq.DestLocationTitle,
				SrcLocation:       rq.SrcLocation,
				DestLocation:      rq.DestLocation,
				CargoName:         rq.CargoName,
				CargoType:         rq.CargoType,
				CargoBulk:         rq.CargoBulk,
				CargoWeight:       rq.CargoWeight,
				CargoCount:        rq.CargoCount,
				CargoValue:        rq.CargoValue,
				ArrivalTime:       rq.ArrivalTime,
				Deposit:           rq.Deposit,
				Price:             rq.Price,
				Consignor:         rq.Consignor,
				Remark:            rq.Remark,
				State:             state,
			})
			if err != nil {
				util.GetLogger().Error("[model-MakeWaybill] - error:%s", err.Error())
				err = errors.New(errors.MSG_Order_Make_Waybill_Error)
				return
			} else {
				rs.WaybillId.Encode(ID_Shard, ID_Ver, ID_Waybill_Category, id)
				rs.PayAmountRS = PayAmountRS{Amount: rq.Deposit}
				if walletDto.Balance >= rq.Deposit {
					rs.PayAmountRS.CanUseWallet = true
				}
			}
		} else {
			err = errors.New(errors.MSG_Wallet_Login_Error)
		}
	}

	return
}

func (o Order) GetWaybillList(mbrId ID, rq *GetWaybillListRQ) (rs WaybillListRS, err error) {
	var id int64
	var mbrType string
	var rowCount int
	var list []data.GetWaybillListDto
	var mbrRepo data.MemberRepo
	var orderRepo data.OrderRepo

	if _, _, _, id, err = mbrId.Decode(); err != nil {
		err = errors.New(errors.MSG_Mbr_Invalid)
		return
	}

	if mbrType, err = mbrRepo.GetMemberType(int(id)); err != nil {
		util.GetLogger().Error("[model-GetWaybillList] - error:%s", err.Error())
	} else {
		if list, rowCount, err = orderRepo.GetWaybillList(int(id), rq.PageNo, rq.PageSize, rq.QueryState, mbrType); err == nil {
			waybills := make([]WaybillList, len(list))
			for idx, dto := range list {
				var waybillId ID
				waybillId.Encode(ID_Shard, ID_Ver, ID_Waybill_Category, int64(dto.Id))
				waybills[idx] = WaybillList{
					Id:           waybillId,
					ShipmentTime: dto.ShipmentTime,
					StartArea:    dto.StartArea,
					EndArea:      dto.EndArea,
					Price:        dto.Price,
					Deposit:      dto.Deposit,
					CargoType:    dto.CargoType,
					CargoName:    dto.CargoName,
					PaymentTerm:  dto.PaymentTerm,
					State:        dto.State,
				}
			}
			rs = WaybillListRS{TotalCount: rowCount, List: waybills}
		} else {
			util.GetLogger().Error("[model-GetWaybillList] - error:%s", err.Error())
			err = nil
		}
	}
	return
}

func (o Order) GetWaybillDetail(waybillId ID) (rs WaybillDetailRS, err error) {
	var id int64
	var repo data.OrderRepo
	var logiRepo data.LogisticsRepo
	var dto data.WaybillDetailDto
	var invoiceInfoDto data.InvoiceInfoDto
	_, _, _, id, err = waybillId.Decode()
	if err != nil {
		err = errors.New(errors.MSG_Order_Invalid_Waybill)
	} else {
		if dto, err = repo.GetWaybillDetail(int(id)); err == nil {
			var carrierPubId, carrierOwnerId, cargoOwnerId ID
			carrierPubId.Encode(ID_Shard, ID_Ver, ID_Carrier_Pub_Category, int64(dto.CarrierPubId))
			carrierOwnerId.Encode(ID_Shard, ID_Ver, ID_Member_Category, int64(dto.CarrierOwnerId))
			cargoOwnerId.Encode(ID_Shard, ID_Ver, ID_Member_Category, int64(dto.CargoOwnerId))
			rs = WaybillDetailRS{
				CargoOwner:        dto.CargoOwner,
				CargoOwnerPhone:   dto.CargoOwnerPhone,
				CargoOwnerId:      cargoOwnerId,
				CarrierOwner:      dto.CarrierOwner,
				CarrierOwnerPhone: dto.CarrierOwnerPhone,
				CarrierOwnerId:    carrierOwnerId,
				StartArea:         dto.StartArea,
				StartAddr:         dto.StartAddr,
				EndArea:           dto.EndArea,
				EndAddr:           dto.EndAddr,
				SrcGeo:            dto.SrcGeo,
				DestGeo:           dto.DestGeo,
				SrcLocationTitle:  dto.SrcLocationTitle,
				DestLocationTitle: dto.DestLocationTitle,
				SrcLocation:       dto.SrcLocation,
				DestLocation:      dto.DestLocation,
				VehicleNo:         dto.VehicleNo,
				VehicleType:       dto.VehicleType,
				VehcileLen:        dto.VehicleLen,
				VehicleLoad:       dto.VehicleLoad,
				ShipmentTime:      dto.ShipmentTime,
				ArrivalTime:       dto.ArrivalTime,
				CarrierPubId:      carrierPubId,
				CargoName:         dto.CargoName,
				CargoType:         dto.CargoType,
				CargoBulk:         dto.CargoBulk,
				CargoWeight:       dto.CargoWeight,
				CargoCount:        dto.CargoCount,
				CargoValue:        dto.CargoValue,
				Deposit:           dto.Deposit,
				Price:             dto.Price,
				Consignor:         dto.Consignor,
				Consignee:         dto.Consignee,
				ConsigneePhone:    dto.ConsigneePhone,
				Remark:            "",
				State:             dto.State,
				NeedInvoice:       dto.NeedInvoice,
				NeedInsurance:     dto.NeedInsurance,
				NeedOilCard:       dto.NeedOilCard,
				PaymentTerm:       dto.PaymentTerm,
				OilCardNo:         dto.OilCardNo,
				LineType:          dto.LineType,
			}
			if dto.NeedInvoice {
				if invoiceInfoDto, err = logiRepo.GetInvoiceInfo(int(id)); err == nil {
					rs.InvoiceInfo = InvoiceInfo{
						Title:         invoiceInfoDto.Title,
						Addr:          invoiceInfoDto.Addr,
						RecvName:      invoiceInfoDto.RecvName,
						ZipCode:       invoiceInfoDto.ZipCode,
						RecvPhone:     invoiceInfoDto.RecvPhone,
						TaxNo:         invoiceInfoDto.TaxNo,
						Amount:        invoiceInfoDto.Amount,
						InvoiceCorp:   invoiceInfoDto.InvoiceCorp,
						BankOfDeposit: invoiceInfoDto.BankOfDeposit,
						BankCardNo:    invoiceInfoDto.BankCardNo,
					}
				} else {
					util.GetLogger().Error("[model-GetWaybillDetail] - error:%s", err.Error())
					err = nil
				}
			}
		} else {
			util.GetLogger().Error("[model-GetWaybillDetail] - error:%s", err.Error())
			err = nil
		}
	}

	return
}

func (o Order) UpdateWaybill(mbrId ID, rq *MakeWaybillRQ) (err error) {
	var mId, wId, pId int64
	var orderRepo data.OrderRepo
	if _, _, _, mId, err = mbrId.Decode(); err != nil {
		err = errors.New(errors.MSG_Mbr_Invalid)
		return
	}

	if _, _, _, pId, err = rq.CarrierPubId.Decode(); err != nil {
		err = errors.New(errors.MSG_Logi_Invalid_Pub_Id)
		return
	}

	if _, _, _, wId, err = rq.Id.Decode(); err != nil {
		err = errors.New(errors.MSG_Order_Invalid_Waybill_ID)
		return
	}

	if err = o.checkValidWaybillRQ(int(pId), rq); err == nil {
		err = orderRepo.UpdateWaybill(&data.WaybillDto{
			Id:                int(wId),
			CargoOwnerId:      int(mId),
			CarrierOwnerId:    rq.CarrierOwnerId,
			CarrierPubId:      int(pId),
			ShipmentTime:      rq.ShipmentTime,
			StartArea:         rq.StartArea,
			StartAddr:         rq.StartAddr,
			EndArea:           rq.EndArea,
			EndAddr:           rq.EndAddr,
			SrcGeo:            rq.SrcGeo,
			DestGeo:           rq.DestGeo,
			SrcLocationTitle:  rq.SrcLocationTitle,
			DestLocationTitle: rq.DestLocationTitle,
			SrcLocation:       rq.SrcLocation,
			DestLocation:      rq.DestLocation,
			CargoName:         rq.CargoName,
			CargoType:         rq.CargoType,
			CargoBulk:         rq.CargoBulk,
			CargoWeight:       rq.CargoWeight,
			CargoCount:        rq.CargoCount,
			CargoValue:        rq.CargoValue,
			Deposit:           rq.Deposit,
			ArrivalTime:       rq.ArrivalTime,
			PaymentTerm:       rq.PaymentTerm,
			Price:             rq.Price,
			Consignor:         rq.Consignor,
			Remark:            rq.Remark,
			NeedInsurance:     rq.NeedInsurance,
			NeedInvoice:       rq.NeedInvoice,
			Consignee:         rq.Consignee,
			ConsigneePhone:    rq.ConsigneePhone,
		})
		if err != nil {
			err = errors.New(errors.MSG_Order_Update_Waybill_Error)
		}
	}
	return
}

func (o Order) UpdateWaybillState(mbrId ID, rq *UpdateWaybillStateWithParamsRQ) (err error) {
	var mId, wId int64
	var repo data.OrderRepo
	var memberRepo data.MemberRepo
	var ownerDto data.WaybillOwnerDto
	if _, _, _, mId, err = mbrId.Decode(); err != nil {
		err = errors.New(errors.MSG_Mbr_Invalid)
		return
	}

	if _, _, _, wId, err = rq.Id.Decode(); err != nil {
		err = errors.New(errors.MSG_Order_Invalid_Waybill_ID)
		return
	}

	if ownerDto, err = repo.GetWaybillOwners(int(wId)); err != nil {
		err = errors.New(errors.MSG_Order_Invalid_Waybill_ID)
		return
	}

	rq.UpdateParam.Id = rq.Id
	//rq.UpdateParam.CarrierOwnerId = int(mId)
	rq.InvoiceCorpId = 1

	if rq.State == O_OnTheWay {
		if !memberRepo.EnsureCarrierOwner(ownerDto.CarrierOwnerId) {
			err = errors.New(errors.MSG_Mbr_Only_Carrier_Owner_Can_Make_Waybill)
			return
		}
	}

	if rq.State == O_OnContract_Wait2Pay {

		if rq.UpdateParam.Price <= 0 {
			err = errors.New(errors.MSG_Order_Price_Lt_Zero)
			return
		}

		if rq.NeedInvoice && !rq.NeedInsurance {
			err = errors.New(errors.MSG_Order_Both_Insurance_Invoice_Required)
			return
		}

		updSucc := true
		if rq.UpdateParam.CarrierPubId != "" {
			if err = o.UpdateWaybill(mbrId, &rq.UpdateParam); err != nil {
				util.GetLogger().Error("[model-UpdateWaybillState] - error:%s	state:%d", err.Error(), rq.State)
			}
			updSucc = err == nil
		}

		if updSucc {
			if err = CancelPaymentIfAny(int(wId)); err != nil {
				util.GetLogger().Error("[model-UpdateWaybillState-topUp] - error:%s	state:%d", err.Error(), rq.State)
			}
			if err = CancelInsuranceIfAny(int(wId)); err != nil {
				util.GetLogger().Error("[model-UpdateWaybillState-topUp] - error:%s	state:%d", err.Error(), rq.State)
			}
			// if err = repo.CancelInvoice(int(wId)); err != nil {
			// 	util.GetLogger().Error("[model-UpdateWaybillState-topUp] - error:%s	state:%d", err.Error(), rq.State)
			// }

			if err = topUp(int(wId), 0); err != nil {
				util.GetLogger().Error("[model-UpdateWaybillState-topUp] - error:%s	state:%d", err.Error(), rq.State)
			}

			var invoice data.InvoiceDto
			if rq.NeedInvoice {
				var logisticsRepo data.LogisticsRepo
				if !logisticsRepo.EnsureInvoiceCorpExists(rq.InvoiceCorpId) {
					err = errors.New(errors.MSG_Order_Invalid_Invoice_Corp)
					return
				}

				invoice = data.InvoiceDto{
					Title:         rq.InvoiceInfo.Title,
					RecvName:      rq.InvoiceInfo.RecvName,
					Addr:          rq.InvoiceInfo.Addr,
					TaxNo:         rq.InvoiceInfo.TaxNo,
					ZipCode:       rq.InvoiceInfo.ZipCode,
					RecvPhone:     rq.InvoiceInfo.RecvPhone,
					BankOfDeposit: rq.InvoiceInfo.BankOfDeposit,
					BankCardNo:    rq.InvoiceInfo.BankCardNo,
					CorpId:        rq.InvoiceCorpId,
					WaybillId:     int(wId),
					MemberId:      int(mId),
				}
			}

			if err = repo.UpdateWaybillStateWithParams(int(wId), int(mId), rq.PaymentTerm, rq.NeedInvoice, rq.NeedInsurance, invoice); err != nil {
				util.GetLogger().Error("[model-UpdateWaybillState] - error:%s	state:%d", err.Error(), rq.State)
				err = errors.New(errors.MSG_Order_Upd_Error)
			}
		}
	} else if rq.State == O_OnShipment {
		var repo data.OrderRepo
		if detail, err := repo.GetWaybillDetail(int(wId)); err == nil {
			saveContract(int(wId), detail, rq.UpdateParam.LogisticCorp, rq.UpdateParam.ContractType, rq.UpdateParam.TaxRatio, rq.UpdateParam.PriceIncludeTax)
			if detail.PaymentTerm == 0 {
				PostInsure(int(wId))
				repo.PayInvoiceOk(int(wId))
				if err = UpdatePlatformBalance(int(wId)); err != nil {
					util.GetLogger().Error("[model-UpdateWaybillState] - error:%s	state:%d", err.Error(), rq.State)
				} else {
					if err = repo.UpdateWaybillState(int(wId), int(mId), int(rq.State)); err != nil {
						util.GetLogger().Error("[model-UpdateWaybillState] - error:%s	state:%d", err.Error(), rq.State)
						err = errors.New(errors.MSG_Order_Upd_Error)
					}
				}
			} else {
				if err = repo.UpdateWaybillState(int(wId), int(mId), int(rq.State)); err != nil {
					util.GetLogger().Error("[model-UpdateWaybillState] - error:%s	state:%d", err.Error(), rq.State)
					err = errors.New(errors.MSG_Order_Upd_Error)
				}
			}
		}
	} else if rq.State == O_Cancel {
		if err = cancel(int(mId), int(wId), ownerDto); err != nil {
			util.GetLogger().Error("[model-UpdateWaybillState] - error:%s	state:%d", err.Error(), rq.State)
			err = errors.New(errors.MSG_Order_Cancel_Error)
		}
	} else if rq.State == O_Complete {
		var repo data.OrderRepo
		if detail, err := repo.GetWaybillDetail(int(wId)); err == nil {
			if detail.PaymentTerm == 0 {
				if err = topUp(int(wId), 1); err == nil {
					if err = repo.CompleteWaybill(int(wId), int(mId)); err != nil {
						util.GetLogger().Error("[model-UpdateWaybillState] - error:%s	state:%d", err.Error(), rq.State)
						err = errors.New(errors.MSG_Order_Upd_Error)
					} else {
						AddPointForOrderCompletion(ownerDto.CarrierOwnerId, int(wId))
						AddPointForOrderCompletion(ownerDto.CargoOwnerId, int(wId))
						addRewardIfAny(int(wId))
					}
				}
			} else {
				if err = repo.CompleteWaybill(int(wId), int(mId)); err != nil {
					util.GetLogger().Error("[model-UpdateWaybillState] - error:%s	state:%d", err.Error(), rq.State)
					err = errors.New(errors.MSG_Order_Upd_Error)
				}
			}
		}
	} else {
		if err = repo.UpdateWaybillState(int(wId), int(mId), int(rq.State)); err != nil {
			util.GetLogger().Error("[model-UpdateWaybillState] - error:%s	state:%d", err.Error(), rq.State)
			err = errors.New(errors.MSG_Order_Upd_Error)
		}
	}

	if err == nil {
		var general General
		var mbrRepo data.MemberRepo
		var loginMemberId int
		if int(mId) == ownerDto.CargoOwnerId {
			loginMemberId = ownerDto.CarrierOwnerId
		} else {
			loginMemberId = ownerDto.CargoOwnerId
		}

		if registerId, platform := mbrRepo.GetLoginRegisterId(loginMemberId); len(registerId) > 0 {
			util.Push(fmt.Sprintf(push_format, rq.Id), registerId, platform)
		}

		ownerDto.ArrivalTime = rq.UpdateParam.ArrivalTime
		o.AddOrderMessage(int(wId), string(rq.Id), ownerDto, rq.State)

		if rq.State == O_OnShipment {
			if len(ownerDto.ConsigneePhone) != 0 {
				vendor := sms.NewYunpianApi(ownerDto.ConsigneePhone, template.ConsigneeAfterContractTplId)
				vendor.Add("name", ownerDto.Consignee).Add("order", string(rq.Id)).Add("area", ownerDto.StartAddr)
				sms.SendTo(vendor)
			}
		}
		if rq.State == O_OnArrival {
			if len(ownerDto.ConsigneePhone) != 0 {
				vendor := sms.NewYunpianApi(ownerDto.ConsigneePhone, template.ConsigneeWhenCargoArrivedTplId)
				vendor.Add("order", string(rq.Id)).Add("date", rq.UpdateParam.ArrivalTime).Add("area", ownerDto.EndAddr)
				sms.SendTo(vendor)
			}
			if len(ownerDto.Consignor) != 0 {
				if split := strings.Split(ownerDto.Consignor, "|"); len(split) == 2 {
					vendor := sms.NewYunpianApi(split[1], template.ConsignorWhenCargoArrivedTplId)
					vendor.Add("order", string(rq.Id)).Add("date", rq.UpdateParam.ArrivalTime).Add("area", ownerDto.EndAddr).Add("phone", general.GetParams("phone"))
					sms.SendTo(vendor)
				}
			}
		}
		if rq.State == O_Cancel {
			var refundAmount float32
			var payRepo data.PayRepo
			var mbrList []data.MemberDto

			if mbrList, err = mbrRepo.GetMemberBriefInfo([]int{loginMemberId}); err == nil && len(mbrList) == 1 {
				refundAmount, _ = payRepo.GetRefundAmountByWaybillId(int(wId))

				vendor := sms.NewYunpianApi(mbrList[0].Phone, template.RefundNotifyTplId)
				vendor.Add("order", string(rq.Id)).Add("date", time.Now().Format("2006-01-02 15:04:05")).Add("amount", fmt.Sprintf("%.2f", refundAmount)).Add("phone", general.GetParams("phone"))
				sms.SendTo(vendor)
			}
		}
	}

	return
}

func (o Order) UpdateOfflineState(mbrID ID, rq *UpdateOfflineWaybillStateWithParamsRQ) (err error) {
	var mid, wid int64
	var repo data.OrderRepo
	var memberRepo data.MemberRepo
	var ownerDto data.WaybillOwnerDto
	var carrierMemberDto data.MemberDto

	if _, _, _, mid, err = mbrID.Decode(); err != nil {
		err = errors.New(errors.MSG_Mbr_Invalid)
		return
	}

	if _, _, _, wid, err = rq.Id.Decode(); err != nil {
		err = errors.New(errors.MSG_Order_Invalid_Waybill_ID)
		return
	}

	if ownerDto, err = repo.GetWaybillOwners(int(wid)); err != nil {
		err = errors.New(errors.MSG_Order_Invalid_Waybill_ID)
		return
	}

	if carrierMemberDto, err = memberRepo.GetMemberInfo(ownerDto.CarrierOwnerId); err != nil {
		err = errors.New(errors.MSG_Mbr_Invalid)
		return
	}

	rq.UpdateParam.Id = rq.Id
	rq.UpdateParam.PaymentTerm = 1
	rq.UpdateParam.NeedInvoice = true
	rq.UpdateParam.NeedInsurance = rq.NeedInsurance
	if rq.UpdateParam.CarrierPubId != "" {
		if err = o.UpdateWaybill(mbrID, &rq.UpdateParam); err != nil {
			util.GetLogger().Error("[model-UpdateOfflineState] - error:%s	state:4", err.Error())
		} else {
			var notInsured bool
			var ratio float64
			var invoiceID int64
			var invoiceAmount float32
			var ratioInsuranceAmount float32
			var minInsuranceAmount float64
			var general General
			var payRepo data.PayRepo

			if rq.NeedInsurance {
				if ratio, err = strconv.ParseFloat(general.GetParams("insurance_ratio"), 32); err == nil {
					ratioInsuranceAmount = float32(util.FormatAmount(float64(rq.UpdateParam.CargoValue) * ratio))
					if minInsuranceAmount, err = strconv.ParseFloat(general.GetParams("min_insurance_amount"), 32); err == nil {
						if float64(ratioInsuranceAmount) < minInsuranceAmount {
							ratioInsuranceAmount = float32(minInsuranceAmount)
						}
					}
				}

				var insuranceRepo data.InsuranceRepo
				if notInsured, err = insuranceRepo.CheckNotInsured(int(wid)); err == nil {
					if notInsured {
						records := make([]InsuranceRecord, 2)
						records[0] = InsuranceRecord{
							MemberId:  ownerDto.CargoOwnerId,
							WaybillId: int(wid),
							Amount:    ratioInsuranceAmount,
							TypeId:    0,
							AgentId:   0,
							ReqData:   "",
						}

						if err = PreInsure(records); err != nil {
							util.GetLogger().Error("[model-UpdateOfflineState] - error%s", err.Error())
							err = errors.New(errors.MSG_Pay_Error)
							return
						}
					}
				} else {
					util.GetLogger().Error("[model-UpdateOfflineState] - error%s", err.Error())
					err = errors.New(errors.MSG_Pay_Error)
					return
				}
			}

			if ratio, err = strconv.ParseFloat(general.GetParams("invoice_ratio"), 32); err == nil {
				invoiceAmount = float32(util.FormatAmount(float64(rq.UpdateParam.Price) / ratio))
			}

			invoice := data.InvoiceDto{
				Title:           rq.InvoiceInfo.Title,
				RecvName:        rq.InvoiceInfo.RecvName,
				Addr:            rq.InvoiceInfo.Addr,
				TaxNo:           rq.InvoiceInfo.TaxNo,
				ZipCode:         rq.InvoiceInfo.ZipCode,
				RecvPhone:       rq.InvoiceInfo.RecvPhone,
				BankOfDeposit:   rq.InvoiceInfo.BankOfDeposit,
				BankCardNo:      rq.InvoiceInfo.BankCardNo,
				PayeeName:       rq.PayeeName,
				PayeeIDCard:     rq.PayeeIDCard,
				ApplyTime:       time.Now().Format("2006-01-02 15:04:05"),
				InsuranceAmount: ratioInsuranceAmount,
				GuaranteeAmount: 0,
				Amount:          invoiceAmount,
				State:           4,
				CorpId:          1,
				WaybillId:       int(wid),
				MemberId:        int(mid),
			}

			invoiceID, err = payRepo.TryAddOfflineInvoiceInfo(invoice)
			if err != nil {
				util.GetLogger().Error("[model-UpdateOfflineState] - error:%s	state:4", err.Error())
				err = errors.New(errors.MSG_INVALID_OFFLINE_PAY)
			}

			payOfflineDto := data.PayOfflineDto{
				MemberID:         ownerDto.CargoOwnerId,
				WaybillID:        int(wid),
				InvoiceID:        int(invoiceID),
				InvoiceIssueCorp: rq.InvoiceIssueCorp,
				BankCard:         rq.BankCard,
				BankName:         rq.BankName,
				PayeeName:        carrierMemberDto.Name,
				PayeeIDCard:      carrierMemberDto.IDCardNo,
			}

			if rq.ContractType != 0 {
				payOfflineDto.PayeeName = rq.PayeeName
				payOfflineDto.PayeeIDCard = rq.PayeeIDCard
			}

			if err = payRepo.AddOfflinePayInfo(payOfflineDto); err != nil {
				util.GetLogger().Error("[model-UpdateOfflineState] - error:%s	state:4", err.Error())
				err = errors.New(errors.MSG_INVALID_OFFLINE_PAY)
			} else {
				if err = repo.UpdateOfflineWaybillState(int(wid), int(mid)); err != nil {
					util.GetLogger().Error("[model-UpdateOfflineState] - error:%s	state:4", err.Error())
					err = errors.New(errors.MSG_Order_Upd_Error)
				} else {
					ownerDto.ArrivalTime = rq.UpdateParam.ArrivalTime
					o.AddOrderMessage(int(wid), string(rq.Id), ownerDto, 4)
				}
			}
		}
	}

	return
}

func (o Order) AddOrderMessage(wid int, waybillID string, owner data.WaybillOwnerDto, state WaybillState) {
	var general General
	var repo data.OrderRepo
	switch state {
	case O_OnTheWay:
		repo.AddMessage(data.WaybillMessageDto{
			WaybillID: wid,
			OwnerID:   owner.CargoOwnerId,
			Text:      util.OnTheWay_Message,
		})
		repo.AddMessage(data.WaybillMessageDto{
			WaybillID: wid,
			OwnerID:   owner.CarrierOwnerId,
			Text:      util.OnTheWay_Message,
		})
	case O_OnContract_Wait2Pay:
		repo.AddMessage(data.WaybillMessageDto{
			WaybillID: wid,
			OwnerID:   owner.CargoOwnerId,
			Text:      util.OnContract_Wait2Pay_Message,
		})
		repo.AddMessage(data.WaybillMessageDto{
			WaybillID: wid,
			OwnerID:   owner.CarrierOwnerId,
			Text:      util.OnContract_Wait2Pay_Message,
		})
	case O_Wait2Confirm:
		repo.AddMessage(data.WaybillMessageDto{
			WaybillID: wid,
			OwnerID:   owner.CargoOwnerId,
			Text:      util.OnContract_Wait2Confirm_Message,
		})
		repo.AddMessage(data.WaybillMessageDto{
			WaybillID: wid,
			OwnerID:   owner.CarrierOwnerId,
			Text:      util.OnContract_Wait2Confirm_Message,
		})
	case O_OnShipment:
		repo.AddMessage(data.WaybillMessageDto{
			WaybillID: wid,
			OwnerID:   owner.CargoOwnerId,
			Text:      template.GetSignalConsigneeAfterContractTemplate(owner.Consignee, waybillID, owner.StartAddr),
		})
		repo.AddMessage(data.WaybillMessageDto{
			WaybillID: wid,
			OwnerID:   owner.CarrierOwnerId,
			Text:      util.OnShipment_Message,
		})
	case O_OnArrival:
		repo.AddMessage(data.WaybillMessageDto{
			WaybillID: wid,
			OwnerID:   owner.CargoOwnerId,
			Text:      template.GetSignalConsignorWhenCargoArrived(waybillID, owner.ArrivalTime, owner.EndAddr, general.GetParams("phone")),
		})
		repo.AddMessage(data.WaybillMessageDto{
			WaybillID: wid,
			OwnerID:   owner.CarrierOwnerId,
			Text:      util.OnArrival_Message,
		})
	case O_Complete:
		repo.AddMessage(data.WaybillMessageDto{
			WaybillID: wid,
			OwnerID:   owner.CargoOwnerId,
			Text:      util.Complete_Message,
		})
		repo.AddMessage(data.WaybillMessageDto{
			WaybillID: wid,
			OwnerID:   owner.CarrierOwnerId,
			Text:      util.Complete_Message,
		})
	case O_Cancel:
		repo.AddMessage(data.WaybillMessageDto{
			WaybillID: wid,
			OwnerID:   owner.CargoOwnerId,
			Text:      util.Cancel_Message,
		})
		repo.AddMessage(data.WaybillMessageDto{
			WaybillID: wid,
			OwnerID:   owner.CarrierOwnerId,
			Text:      util.Cancel_Message,
		})
	}

}

func (o Order) UpdateInvoice(memberId, waybillId ID, invoice InvoiceInfo) (err error) {
	var mid, wid int64
	var repo data.OrderRepo
	if _, _, _, mid, err = memberId.Decode(); err != nil {
		err = errors.New(errors.MSG_Mbr_Invalid)
		return
	}
	if _, _, _, wid, err = waybillId.Decode(); err != nil {
		err = errors.New(errors.MSG_Order_Invalid_Waybill_ID)
		return
	}
	err = repo.UpdateInvoice(int(mid), int(wid), data.InvoiceDto{
		Title:         invoice.Title,
		Addr:          invoice.Addr,
		RecvName:      invoice.RecvName,
		TaxNo:         invoice.TaxNo,
		RecvPhone:     invoice.RecvPhone,
		ZipCode:       invoice.ZipCode,
		WaybillId:     int(wid),
		BankOfDeposit: invoice.BankOfDeposit,
		BankCardNo:    invoice.BankCardNo,
	})
	return
}

func (o Order) UpdateOilCard(waybillId, memberId ID, needOilCard bool, oilcardNo string) (err error) {
	var mid, wid int64
	if _, _, _, wid, err = waybillId.Decode(); err != nil {
		err = errors.New(errors.MSG_Order_Invalid_Waybill_ID)
		return
	}
	if _, _, _, mid, err = memberId.Decode(); err != nil {
		err = errors.New(errors.MSG_Mbr_Invalid)
		return
	}
	var repo data.OrderRepo
	if err = repo.UpdateOilCard(int(wid), int(mid), needOilCard, oilcardNo); err != nil {
		util.GetLogger().Error("[order.UpdateOilCard] - error%s", err.Error())
		err = errors.New(errors.MSG_Order_Update_OilCard_Error)
	}
	return
}

func (o Order) ViewContract(mbrId ID, rq *MakeWaybillRQ) (contract string) {
	var err error
	var mId, wId int64
	var mbrRepo data.MemberRepo
	var orderRepo data.OrderRepo
	if _, _, _, mId, err = mbrId.Decode(); err != nil {
		err = errors.New(errors.MSG_Mbr_Invalid)
		return
	}

	if _, _, _, wId, err = rq.Id.Decode(); err != nil {
		err = errors.New(errors.MSG_Mbr_Invalid)
		return
	}

	if mbrType, err := mbrRepo.GetMemberType(int(mId)); err == nil {
		if waybillDetail, err := orderRepo.GetWaybillDetail(int(wId)); err == nil {
			if mbrType == "1" || mbrType == "3" {
				if waybillDetail.State >= 4 {
					rq = &MakeWaybillRQ{
						Id:              rq.Id,
						StartArea:       waybillDetail.StartArea,
						StartAddr:       waybillDetail.StartAddr,
						EndArea:         waybillDetail.EndArea,
						EndAddr:         waybillDetail.EndAddr,
						ShipmentTime:    waybillDetail.ShipmentTime,
						CargoName:       waybillDetail.CargoName,
						CargoType:       waybillDetail.CargoType,
						CargoBulk:       waybillDetail.CargoBulk,
						CargoWeight:     waybillDetail.CargoWeight,
						CargoCount:      waybillDetail.CargoCount,
						CargoValue:      waybillDetail.CargoValue,
						ArrivalTime:     waybillDetail.ArrivalTime,
						Deposit:         waybillDetail.Deposit,
						Price:           waybillDetail.Price,
						Consignor:       waybillDetail.Consignor,
						Consignee:       waybillDetail.Consignee,
						ConsigneePhone:  waybillDetail.ConsigneePhone,
						Remark:          waybillDetail.Remark,
						NeedInvoice:     waybillDetail.NeedInvoice,
						NeedInsurance:   waybillDetail.NeedInsurance,
						PaymentTerm:     waybillDetail.PaymentTerm,
						NeedOilCard:     waybillDetail.NeedOilCard,
						State:           waybillDetail.State,
						TaxRatio:        rq.TaxRatio,
						PriceIncludeTax: rq.PriceIncludeTax,
						LogisticCorp:    rq.LogisticCorp,
						ContractType:    rq.ContractType,
					}
				}

				//var ratio float64
				//var general General
				totalBillAmount := float64(rq.Deposit + rq.Price)
				if rq.NeedInvoice {
					ratio := 0.93
					if rq.TaxRatio != 0 {
						ratio = float64(rq.TaxRatio)
					}
					//mbrDto, err := mbrRepo.GetMemberInfo(waybillDetail.CarrierOwnerId)
					//if err == nil {
					//	if mbrDto.TypeId == 4 {
					//		//ratio = 0.922
					//		ratio = 0.93
					//	}
					//}
					//totalBillAmount = util.FormatAmount(float64(rq.Deposit+rq.Price) / ratio)
					if rq.PriceIncludeTax != 0 {
						totalBillAmount = float64(rq.PriceIncludeTax)
					} else {
						totalBillAmount = float64(rq.Deposit+rq.Price) / ratio
					}

					//if ratio, err = strconv.ParseFloat(general.GetParams("invoice_ratio"), 32); err == nil {
					//	totalBillAmount = util.FormatAmount(float64(rq.Deposit+rq.Price) / ratio)
					//}
				}

				if rq.PaymentTerm == 0 {
					contract = o.viewTransportContract(int(mId), int(wId), rq, mbrType, float32(totalBillAmount))
				} else {
					contract = o.viewOfflineTransportContract(int(mId), int(wId), rq, mbrType, float32(totalBillAmount))
				}

			} else {
				if !waybillDetail.NeedInvoice {
					rq = &MakeWaybillRQ{
						Id:             rq.Id,
						StartArea:      waybillDetail.StartArea,
						StartAddr:      waybillDetail.StartAddr,
						EndArea:        waybillDetail.EndArea,
						EndAddr:        waybillDetail.EndAddr,
						ShipmentTime:   waybillDetail.ShipmentTime,
						CargoName:      waybillDetail.CargoName,
						CargoType:      waybillDetail.CargoType,
						CargoBulk:      waybillDetail.CargoBulk,
						CargoWeight:    waybillDetail.CargoWeight,
						CargoCount:     waybillDetail.CargoCount,
						CargoValue:     waybillDetail.CargoValue,
						ArrivalTime:    waybillDetail.ArrivalTime,
						Deposit:        waybillDetail.Deposit,
						Price:          waybillDetail.Price,
						Consignor:      waybillDetail.Consignor,
						Consignee:      waybillDetail.Consignee,
						ConsigneePhone: waybillDetail.ConsigneePhone,
						Remark:         waybillDetail.Remark,
						NeedInvoice:    waybillDetail.NeedInvoice,
						NeedInsurance:  waybillDetail.NeedInsurance,
						NeedOilCard:    waybillDetail.NeedOilCard,
						PaymentTerm:    waybillDetail.PaymentTerm,
						State:          waybillDetail.State,
						LogisticCorp:   rq.LogisticCorp,
					}
					var general General
					totalBillAmount := float64(rq.Deposit + rq.Price)
					if ratio, err := strconv.ParseFloat(general.GetParams("platform_ratio"), 32); err == nil {
						totalBillAmount = totalBillAmount * (1 - ratio)
					}
					contract = o.viewTransportContract(int(mId), int(wId), rq, mbrType, float32(totalBillAmount))
				} else {
					if rq.PaymentTerm == 0 {
						contract = o.viewPowerOfAttorney(int(mId), int(wId), rq.Id, waybillDetail)
					} else {
						contract = o.viewOfflinePowerOfAttorney(int(mId), int(wId), rq.Id, waybillDetail, rq.ContractType, mbrType)
					}

				}
			}
		}
	} else {
		util.GetLogger().Error("[model-ViewContract] - error:%s", err)
	}
	return
}

func (o Order) GetWaybillMessage(memberID ID) (messages []WaybillMessage) {
	var err error
	var mid int64
	var list []data.WaybillMessageDto
	if _, _, _, mid, err = memberID.Decode(); err != nil {
		err = errors.New(errors.MSG_Mbr_Invalid)
		return
	}
	var repo data.OrderRepo
	if list, err = repo.GetWaybillMessage(int(mid)); err == nil {
		messages = make([]WaybillMessage, len(list))
		for i, val := range list {
			messages[i] = WaybillMessage{Text: val.Text, CreateTime: val.CreateTime}
		}
	} else {
		util.GetLogger().Error("[model-GetWaybillMessage] - error:%s", err)
	}
	return
}

func (o Order) UpdateWaybillScore(waybillID ID, score int, comment string) error {
	var wid int64
	var err error
	if _, _, _, wid, err = waybillID.Decode(); err != nil {
		return errors.New(errors.MSG_Mbr_Invalid)
	}
	var repo data.OrderRepo
	if err = repo.UpdateWaybillScore(int(wid), score, comment); err != nil {
		util.GetLogger().Error("[model-UpdateWaybillScore] - error:%s", err)
		err = errors.New(errors.MSG_Order_Update_Waybill_Score_Failed)
	}
	return err
}

func (o Order) GetWaybillScoreList(carrierOwnerID ID) (list []WaybillScore) {
	var cid int64
	var err error
	var repo data.OrderRepo
	if _, _, _, cid, err = carrierOwnerID.Decode(); err == nil {
		if scores, err := repo.GetWaybillScoreList(int(cid)); err == nil {
			list = make([]WaybillScore, len(scores))
			for i, score := range scores {
				list[i] = WaybillScore{NickName: score.NickName, Score: score.Score, CreateTime: score.CreateTime, Comment: score.Comment}
			}
		} else {
			util.GetLogger().Error("[model-GetWaybillScoreList] - error:%s", err)
		}
	}
	return
}

func topUp(waybillId, payFor int) (err error) {
	var orderRepo data.OrderRepo
	var payRepo data.PayRepo
	var paymentDto data.PaymentDto
	var ownerDto data.WaybillOwnerDto
	var notToppedUp bool
	if paymentDto, err = payRepo.GetPaymentDetailByWaybillId(waybillId, payFor, 1); err == nil {
		if ownerDto, err = orderRepo.GetWaybillOwners(waybillId); err == nil {
			typeId := 2
			if payFor == 1 {
				typeId = 6
			}

			if notToppedUp, err = payRepo.CheckWalletNotToppedUp(ownerDto.CarrierOwnerId, typeId, fmt.Sprintf("%x", md5.Sum([]byte(string(waybillId))))); notToppedUp && err == nil {
				var w Wallet
				var general General
				var ratio float64
				var amount float32
				var walletAmount float32
				var detail data.WaybillDetailDto

				if paymentDto.PayFor == 0 { //pay deposit
					amount = paymentDto.DepositAmount
				} else if paymentDto.PayFor == 1 { //pay waybill
					amount = paymentDto.BillAmount
				}

				// 平台实际收入是运费*平台费率
				// eg: 10000纯运费，100订金，如果包含油卡，实际平台收入=10100 * 0.015
				// 车主油卡收入: 10100 * 0.4
				// 车主钱包收入: 10100 * 0.6 - 平台收入 - 订金*0.015

				if ratio, err = strconv.ParseFloat(general.GetParams("platform_ratio"), 32); err == nil {
					if detail, err = orderRepo.GetWaybillDetail(waybillId); err == nil {
						incoming := amount * float32(ratio)
						pamount := amount * float32(1-ratio)
						if typeId == 6 && detail.NeedOilCard {
							total := float32(detail.Deposit + detail.Price)
							totalWalletAmount := total * float32(1-ratio)
							depositWallAmount := detail.Deposit * float32(1-ratio)
							if ratio, err = strconv.ParseFloat(general.GetParams("oilcard_ratio"), 32); err == nil {
								oilcardAmount := total * float32(ratio)
								walletAmount = totalWalletAmount - oilcardAmount - depositWallAmount
								var oilcard OilCard
								err = oilcard.Charge(ownerDto.CarrierOwnerId, waybillId, oilcardAmount, detail.OilCardNo, "")
							}
						} else {
							walletAmount = amount - incoming
						}
						if err == nil {
							if err = w.TopUp(ownerDto.CarrierOwnerId, typeId, waybillId, 0, walletAmount, pamount, incoming, detail.NeedOilCard, detail.OilCardNo); err != nil {
								util.GetLogger().Error("[model-Waybill-TopUp] - error:%s", err.Error())
								err = errors.New(errors.MSG_Order_Upd_Error)
							}
						}
					}
				}

				if err != nil {
					util.GetLogger().Error("[model-Waybill-TopUp] - error:%s", err.Error())
					err = errors.New(errors.MSG_Order_Upd_Error)
				}
			}
		} else {
			util.GetLogger().Error("[model-Waybill-TopUp] - error:%s", err.Error())
			err = errors.New(errors.MSG_Order_Upd_Error)
		}
	} else {
		util.GetLogger().Error("[model-Waybill-TopUp] - error:%s", err.Error())
		err = errors.New(errors.MSG_Order_Upd_Error)
	}
	return
}

func refund(waybillId, refundFor int) (err error) {
	var orderRepo data.OrderRepo
	var payRepo data.PayRepo
	var paymentDto data.PaymentDto
	var ownerDto data.WaybillOwnerDto
	var notToppedUp, notRefund bool
	if paymentDto, err = payRepo.GetPaymentDetailByWaybillId(waybillId, refundFor, 1); err == nil {
		if ownerDto, err = orderRepo.GetWaybillOwners(waybillId); err == nil {
			if notToppedUp, err = payRepo.CheckWalletNotToppedUp(ownerDto.CarrierOwnerId, 4, fmt.Sprintf("%x", md5.Sum([]byte(string(waybillId))))); notToppedUp && err == nil {
				if notRefund, err = payRepo.CheckNotRefund(waybillId, refundFor); notRefund && err == nil {
					//退款的时候，应该退所有钱，不只是订金和运费
					if err = Refund(ownerDto.CargoOwnerId, waybillId, refundFor, paymentDto.TotalAmount, paymentDto.TotalAmount); err != nil {
						util.GetLogger().Error("[model-Waybill-refund] - error:%s", err.Error())
					}
				}
			}
		} else {
			util.GetLogger().Error("[model-Waybill-refund] - error:%s", err.Error())
		}
	} else {
		util.GetLogger().Error("[model-Waybill-refund] - error:%s", err.Error())
	}
	return
}

func cancel(memberId, waybillId int, ownerDto data.WaybillOwnerDto) (err error) {
	var waybillState int
	var repo data.OrderRepo

	if detail, err := repo.GetWaybillDetail(waybillId); err == nil {
		if detail.PaymentTerm == 0 {
			if waybillState, _, err = repo.GetWaybillState(int(waybillId)); err == nil {
				switch waybillState {
				case 0:
					fallthrough
				case 3:
					err = repo.CancelWaybill(int(waybillId), int(memberId))
				case 1:
					if err = refund(waybillId, 0); err == nil {
						err = repo.CancelWaybill(int(waybillId), int(memberId))
					}
				case 2:
					if detail.Deposit != 0 {
						if memberId == ownerDto.CarrierOwnerId {
							err = refund(waybillId, 0)
						}
						if memberId == ownerDto.CargoOwnerId {
							err = topUp(waybillId, 0)
						}
					}
					if err == nil {
						err = repo.CancelWaybill(int(waybillId), int(memberId))
					}
				case 4:
					if err = refund(waybillId, 1); err == nil {
						err = repo.CancelWaybill(int(waybillId), int(memberId))
					}
				}
			}
		} else {
			err = repo.CancelWaybill(int(waybillId), int(memberId))
		}
	}

	return
}

func (o Order) viewTransportContract(mId, wId int, rq *MakeWaybillRQ, mbrType string, totalBillAmount float32) (contract string) {
	if len(transport_contract) != 0 {
		var orderRepo data.OrderRepo
		var mbrRepo data.MemberRepo
		var logiRepo data.LogisticsRepo
		var tc Contract
		var l Logistics
		var g General
		var err error
		var detail data.WaybillDetailDto
		var invoiceAddr data.InvoiceAddrDto
		if detail, err = orderRepo.GetWaybillDetail(wId); err == nil {
			if detail.CarrierOwnerId == mId || detail.CargoOwnerId == mId {
				tc = Contract{
					CargoOwnerName:   detail.CargoOwner,
					CarrierOwnerName: detail.CarrierOwner,
					VehicleNo:        detail.VehicleNo,
					CargoName:        rq.CargoName,
					CargoType:        rq.CargoType,
					CargoValue:       rq.CargoValue,
					Price:            rq.Price,
					Weight:           rq.CargoWeight,
					Bulk:             rq.CargoBulk,
					CargoCount:       rq.CargoCount,
					Remark:           rq.Remark,
					Consignor:        rq.Consignor,
					Consignee:        rq.Consignee,
					ConsigneePhone:   rq.ConsigneePhone,
					CargoRcvUnitName: "",
					CargoRcvAddr:     rq.EndAddr,
					ArrivalTime:      rq.ArrivalTime,
					PaymentTerm:      rq.PaymentTerm,
					StartArea:        rq.StartArea,
					EndArea:          rq.EndArea,
					CarrierOwnerAddr: "",
					DrivingLicneseNo: "",
					NeedOilCard:      detail.NeedOilCard,
				}

				if cro, err := mbrRepo.GetCarrierOwner(detail.CarrierOwnerId); err == nil {
					tc.CarrierOwnerName = cro.Name
					tc.CarrierOwnerPhone = cro.Phone
					tc.DrivingLicneseNo = cro.IDCardNo
				}

				if mbrType == "1" {
					if cgo, err := mbrRepo.GetCargoOwner(detail.CargoOwnerId); err == nil {
						tc.IsCorp = cgo.IsCorp
						tc.CargoOwnerName = cgo.Name
						tc.CargoCorpName = cgo.CorpName
						tc.CargoOwnerPhone = cgo.Phone
						tc.CargoCorpPhone = cgo.Telpehone
					}
				} else {
					if corp, err := logiRepo.GetLogisticsCorpDetail(detail.CargoOwnerId); err == nil {
						tc.IsCorp = true
						tc.CargoOwnerName = corp.OwnerName
						tc.CargoCorpName = corp.CorpName
						tc.CargoOwnerPhone = corp.OwnerPhone
					}
				}

				if rq.NeedInvoice {
					if invoiceCorp, ok := l.GetInvoiceCorp("1"); ok {
						tc.InvoiceCorpName = invoiceCorp.Name
					}
				}
			}
		}

		var partyAName, partyBName, cargoTypeName, paymentTermName string
		if tc.IsCorp {
			partyAName = tc.CargoCorpName
			if partyAName == "" {
				if !rq.NeedInvoice {
					partyAName = tc.CargoOwnerName
				} else {
					if invoiceAddr, err = logiRepo.GetLastInvoiceAddr(mId); err == nil {
						partyAName = invoiceAddr.Title
					}
				}
			}
		} else {
			partyAName = tc.CargoOwnerName
		}

		if rq.NeedInvoice {
			partyBName = tc.InvoiceCorpName
		} else {
			partyBName = tc.CarrierOwnerName
		}
		partyCName := "江苏快来运信息科技有限责任公司"

		startArea := l.GetAreaFullName(tc.StartArea)
		endArea := l.GetAreaFullName(tc.EndArea)

		cargoTypeMap := g.GetDict("106")
		if len(cargoTypeMap) > 0 {
			cargoTypeName = cargoTypeMap[strconv.Itoa(tc.CargoType)]
		}
		paymentTermMap := g.GetDict("121")
		if len(paymentTermMap) > 0 {
			paymentTermName = paymentTermMap[strconv.Itoa(tc.PaymentTerm)]
		}

		var ratio float64
		var general General
		var ratioInsuranceAmount float32
		var minInsuranceAmount, fixedInsuranceAmount float64

		if rq.NeedInsurance {
			if ratio, err = strconv.ParseFloat(general.GetParams("insurance_ratio"), 32); err == nil {
				ratioInsuranceAmount = float32(util.FormatAmount(float64(rq.CargoValue) * ratio))
				if minInsuranceAmount, err = strconv.ParseFloat(general.GetParams("min_insurance_amount"), 32); err == nil {
					if float64(ratioInsuranceAmount) < minInsuranceAmount {
						ratioInsuranceAmount = float32(minInsuranceAmount)
					}
				}
			}
			fixedInsuranceAmount, err = strconv.ParseFloat(general.GetParams("insurance_fixed_amount"), 32)
		}

		var arrivalTime string
		if len(tc.ArrivalTime) >= 10 {
			arrivalTime = tc.ArrivalTime[:10]
		}

		showOildCardAmount := ""
		if detail.CarrierOwnerId == mId {
			if detail.NeedOilCard {
				if ratio, err = strconv.ParseFloat(general.GetParams("oilcard_ratio"), 32); err == nil {
					total := float32(rq.Deposit + rq.Price)
					oilcardAmount := total * float32(ratio)
					walletAmount := totalBillAmount - oilcardAmount
					showOildCardAmount = fmt.Sprintf("(其中现金%.2f元，油卡充值%.2f元)", walletAmount, oilcardAmount)
				}
			}
		}

		if rq.State > 5 {
			if contract, err = orderRepo.GetContract(int(mId), int(wId), 1); err != nil {
				util.GetLogger().Error("[model-viewTransportContract] - error:%s", err.Error())
			}
		} else {
			contract = fmt.Sprintf(transport_contract, rq.Id, partyAName, partyBName, tc.CargoOwnerName,
				tc.CargoOwnerPhone, tc.VehicleNo, tc.CarrierOwnerName, tc.CarrierOwnerPhone, tc.DrivingLicneseNo, partyCName, startArea, endArea, tc.CargoName, cargoTypeName, tc.CargoValue, totalBillAmount, showOildCardAmount, ratioInsuranceAmount, fixedInsuranceAmount, tc.Weight, tc.Bulk, tc.CargoCount,
				tc.Remark, tc.Consignee, tc.ConsigneePhone, tc.CargoRcvAddr, arrivalTime,
				paymentTermName, partyAName, partyBName, partyCName, detail.OrderTime)
		}
	}
	return
}

func (o Order) viewOfflineTransportContract(mId, wId int, rq *MakeWaybillRQ, mbrType string, totalBillAmount float32) (contract string) {
	if len(offline_transport_contract) != 0 {
		var orderRepo data.OrderRepo
		var mbrRepo data.MemberRepo
		var logiRepo data.LogisticsRepo
		var tc Contract
		var l Logistics
		var g General
		var err error
		var detail data.WaybillDetailDto
		var invoiceAddr data.InvoiceAddrDto
		if detail, err = orderRepo.GetWaybillDetail(wId); err == nil {
			if detail.CarrierOwnerId == mId || detail.CargoOwnerId == mId {
				tc = Contract{
					CargoOwnerName:   detail.CargoOwner,
					CarrierOwnerName: detail.CarrierOwner,
					VehicleNo:        detail.VehicleNo,
					CargoName:        rq.CargoName,
					CargoType:        rq.CargoType,
					CargoValue:       rq.CargoValue,
					Price:            rq.Price,
					Weight:           rq.CargoWeight,
					Bulk:             rq.CargoBulk,
					CargoCount:       rq.CargoCount,
					Remark:           rq.Remark,
					Consignor:        rq.Consignor,
					Consignee:        rq.Consignee,
					ConsigneePhone:   rq.ConsigneePhone,
					CargoRcvUnitName: "",
					CargoRcvAddr:     rq.EndAddr,
					ArrivalTime:      rq.ArrivalTime,
					PaymentTerm:      rq.PaymentTerm,
					StartArea:        rq.StartArea,
					EndArea:          rq.EndArea,
					CarrierOwnerAddr: "",
					DrivingLicneseNo: "",
					NeedOilCard:      detail.NeedOilCard,
				}

				if cro, err := mbrRepo.GetCarrierOwner(detail.CarrierOwnerId); err == nil {
					tc.CarrierOwnerName = cro.Name
					tc.CarrierOwnerPhone = cro.Phone
					tc.DrivingLicneseNo = cro.IDCardNo

					mbrDto, err := mbrRepo.GetMemberInfo(detail.CarrierOwnerId)
					if err == nil {
						if mbrDto.TypeId == 4 {
							tc.VehicleNo = cro.ShipName
						}
					}
				}

				if mbrType == "1" {
					if cgo, err := mbrRepo.GetCargoOwner(detail.CargoOwnerId); err == nil {
						tc.IsCorp = cgo.IsCorp
						tc.CargoOwnerName = cgo.Name
						tc.CargoCorpName = cgo.CorpName
						tc.CargoOwnerPhone = cgo.Phone
						tc.CargoCorpPhone = cgo.Telpehone
					}
				} else {
					if corp, err := logiRepo.GetLogisticsCorpDetail(detail.CargoOwnerId); err == nil {
						tc.IsCorp = true
						tc.CargoOwnerName = corp.OwnerName
						tc.CargoCorpName = corp.CorpName
						tc.CargoOwnerPhone = corp.OwnerPhone
					}
				}

				if rq.NeedInvoice {
					if len(rq.LogisticCorp) != 0 {
						tc.InvoiceCorpName = rq.LogisticCorp
					} else {
						if invoiceCorp, ok := l.GetInvoiceCorp("1"); ok {
							tc.InvoiceCorpName = invoiceCorp.Name
						}
					}
				}
			}
		}

		var partyAName, partyBName, cargoTypeName string
		if tc.IsCorp {
			partyAName = tc.CargoCorpName
			if partyAName == "" {
				if !rq.NeedInvoice {
					partyAName = tc.CargoOwnerName
				} else {
					if invoiceAddr, err = logiRepo.GetLastInvoiceAddr(mId); err == nil {
						partyAName = invoiceAddr.Title
					}
				}
			}
		} else {
			partyAName = tc.CargoOwnerName
		}

		if rq.NeedInvoice {
			partyBName = tc.InvoiceCorpName
		} else {
			partyBName = tc.CarrierOwnerName
		}

		var insuranceDesc string
		var general General
		var ratio float64
		var ratioInsuranceAmount float32
		var minInsuranceAmount float64

		if rq.NeedInsurance {
			if ratio, err = strconv.ParseFloat(general.GetParams("insurance_ratio"), 32); err == nil {
				ratioInsuranceAmount = float32(util.FormatAmount(float64(rq.CargoValue) * ratio))
				if minInsuranceAmount, err = strconv.ParseFloat(general.GetParams("min_insurance_amount"), 32); err == nil {
					if float64(ratioInsuranceAmount) < minInsuranceAmount {
						ratioInsuranceAmount = float32(minInsuranceAmount)
					}
				}
			}
			insuranceDesc = fmt.Sprintf("<div> 保险费：<u>%.2f元</u></div>", ratioInsuranceAmount)
		}

		startArea := l.GetAreaFullName(tc.StartArea)
		endArea := l.GetAreaFullName(tc.EndArea)

		cargoTypeMap := g.GetDict("106")
		if len(cargoTypeMap) > 0 {
			cargoTypeName = cargoTypeMap[strconv.Itoa(tc.CargoType)]
		}

		var arrivalTime string
		if len(tc.ArrivalTime) >= 10 {
			arrivalTime = tc.ArrivalTime[:10]
		}

		if rq.State > 5 {
			if contract, err = orderRepo.GetContract(int(mId), int(wId), 1); err != nil {
				util.GetLogger().Error("[model-viewTransportContract] - error:%s", err.Error())
			}
		} else {
			delegateRemark := ""
			if rq.ContractType == 0 {
				delegateRemark = fmt.Sprintf("<div>其中委托甲方直接支付现金<u>%.2f元</u>给驾驶员<u>%s</u></div>", detail.Price, tc.CarrierOwnerName)
			}
			contract = fmt.Sprintf(offline_transport_contract, rq.Id, partyAName, partyBName, tc.CargoOwnerName,
				tc.CargoOwnerPhone, tc.VehicleNo, tc.CarrierOwnerName, tc.CarrierOwnerPhone, tc.DrivingLicneseNo, startArea, endArea, tc.CargoName, cargoTypeName, tc.CargoValue, totalBillAmount, insuranceDesc, delegateRemark, tc.Weight, tc.Bulk, tc.CargoCount,
				"", tc.Consignee, tc.ConsigneePhone, tc.CargoRcvAddr, arrivalTime, partyAName, partyBName, detail.OrderTime)
		}
	}
	return
}

func (o Order) viewPowerOfAttorney(mId, wId int, waybillId ID, detail data.WaybillDetailDto) (contract string) {
	if len(power_of_attorney) != 0 {
		var mbrRepo data.MemberRepo
		var orderRepo data.OrderRepo
		var tc Contract
		var l Logistics
		var g General
		var err error

		if detail.CarrierOwnerId == mId || detail.CargoOwnerId == mId {
			tc = Contract{
				CarrierOwnerName: detail.CarrierOwner,
				VehicleNo:        detail.VehicleNo,
				CargoName:        detail.CargoName,
				CargoType:        detail.CargoType,
				CargoCount:       detail.CargoCount,
				CargoValue:       detail.CargoValue,
				Price:            detail.Price,
				Weight:           detail.CargoWeight,
				Bulk:             detail.CargoBulk,
				Remark:           detail.Remark,
				Consignor:        detail.Consignor,
				Consignee:        detail.Consignee,
				ConsigneePhone:   detail.ConsigneePhone,
				CargoRcvUnitName: "",
				CargoRcvAddr:     detail.EndAddr,
				ArrivalTime:      detail.ArrivalTime,
				PaymentTerm:      detail.PaymentTerm,
				StartArea:        detail.StartArea,
				EndArea:          detail.EndArea,
				CarrierOwnerAddr: "",
				DrivingLicneseNo: "",
				NeedOilCard:      detail.NeedOilCard,
			}

			if cro, err := mbrRepo.GetCarrierOwner(detail.CarrierOwnerId); err == nil {
				tc.CarrierOwnerName = cro.Name
				tc.CarrierOwnerPhone = cro.Phone
				tc.DrivingLicneseNo = cro.IDCardNo
			}

			if invoice, err := orderRepo.GetInvoice(wId); err == nil {
				if invoiceCorp, ok := l.GetInvoiceCorp(strconv.Itoa(invoice.CorpId)); ok {
					tc.InvoiceCorpName = invoiceCorp.Name
				}
			} else {
				util.GetLogger().Error("[model-viewPowerOfAttorney] - error:%s", err.Error())
			}
		}

		var cargoTypeName, paymentTermName string

		startArea := l.GetAreaFullName(tc.StartArea)
		endArea := l.GetAreaFullName(tc.EndArea)

		cargoTypeMap := g.GetDict("106")
		if len(cargoTypeMap) > 0 {
			cargoTypeName = cargoTypeMap[strconv.Itoa(tc.CargoType)]
		}
		paymentTermMap := g.GetDict("121")
		if len(paymentTermMap) > 0 {
			paymentTermName = paymentTermMap[strconv.Itoa(tc.PaymentTerm)]
		}

		var p PayAmountDetail
		var general General
		p, _ = GetPaymentAmountDetailByWaybillId(int(wId))
		totalBillAmount := float64(detail.Deposit + p.BillAmount)
		if ratio, err := strconv.ParseFloat(general.GetParams("platform_ratio"), 32); err == nil {
			totalBillAmount = totalBillAmount * (1 - ratio)
		}

		if detail.State <= 3 {
			var ratio float64
			var ratioInsuranceAmount float32
			var minInsuranceAmount, fixedInsuranceAmount float64
			if detail.NeedInsurance {
				if ratio, err = strconv.ParseFloat(general.GetParams("insurance_ratio"), 32); err == nil {
					ratioInsuranceAmount = float32(util.FormatAmount(float64(detail.CargoValue) * ratio))
					if minInsuranceAmount, err = strconv.ParseFloat(general.GetParams("min_insurance_amount"), 32); err == nil {
						if float64(ratioInsuranceAmount) < minInsuranceAmount {
							ratioInsuranceAmount = float32(minInsuranceAmount)
						}
					}
				}
				fixedInsuranceAmount, err = strconv.ParseFloat(general.GetParams("insurance_fixed_amount"), 32)
			}
			p.InsuranceAmount = ratioInsuranceAmount
			p.GuaranteeAmount = float32(fixedInsuranceAmount)

			totalBillAmount = float64(detail.Deposit + detail.Price)
			if ratio, err := strconv.ParseFloat(general.GetParams("platform_ratio"), 32); err == nil {
				totalBillAmount = totalBillAmount * (1 - ratio)
			}
		}

		var arrivalTime string
		if len(tc.ArrivalTime) >= 10 {
			arrivalTime = tc.ArrivalTime[:10]
		}

		showOildCardAmount := ""
		if detail.CarrierOwnerId == mId {
			if detail.NeedOilCard {
				var ratio float64
				if ratio, err = strconv.ParseFloat(general.GetParams("oilcard_ratio"), 32); err == nil {
					total := float32(detail.Deposit + detail.Price)
					oilcardAmount := total * float32(ratio)
					walletAmount := float32(totalBillAmount) - oilcardAmount
					showOildCardAmount = fmt.Sprintf("(其中现金%.2f元，油卡充值%.2f元)", walletAmount, oilcardAmount)
				}
			}
		}

		if detail.State > 5 {
			if contract, err = orderRepo.GetContract(int(mId), int(wId), 1); err != nil {
				util.GetLogger().Error("[model-viewTransportContract] - error:%s", err.Error())
			}
		} else {
			var l data.LogisticsRepo
			totalInvoiceAmount, err := orderRepo.TotalInvoiceAmount()
			if err != nil {
				return
			}
			name, err := l.GetInvoiceIssueCorp(totalInvoiceAmount)
			if err != nil {
				return
			}

			contract = fmt.Sprintf(power_of_attorney, waybillId, name, tc.CarrierOwnerName,
				tc.VehicleNo, tc.CarrierOwnerPhone, tc.DrivingLicneseNo, startArea, endArea, tc.CargoName, cargoTypeName, tc.CargoValue,
				totalBillAmount, showOildCardAmount, p.InsuranceAmount, p.GuaranteeAmount, tc.Weight, tc.Bulk, tc.CargoCount,
				tc.Remark, tc.Consignee, tc.ConsigneePhone, tc.CargoRcvAddr, arrivalTime,
				paymentTermName, name, tc.CarrierOwnerName, detail.OrderTime)
		}
	}
	return
}

func (o Order) viewOfflinePowerOfAttorney(mId, wId int, waybillId ID, detail data.WaybillDetailDto, contractType int, mbrType string) (contract string) {
	if len(offline_power_of_attorney) != 0 {
		var mbrRepo data.MemberRepo
		var orderRepo data.OrderRepo
		var payRepo data.PayRepo

		var tc Contract
		var l Logistics
		var g General
		var err error

		if detail.CarrierOwnerId == mId || detail.CargoOwnerId == mId {
			tc = Contract{
				CarrierOwnerName: detail.CarrierOwner,
				VehicleNo:        detail.VehicleNo,
				CargoName:        detail.CargoName,
				CargoType:        detail.CargoType,
				CargoCount:       detail.CargoCount,
				CargoValue:       detail.CargoValue,
				Price:            detail.Price,
				Weight:           detail.CargoWeight,
				Bulk:             detail.CargoBulk,
				Remark:           detail.Remark,
				Consignor:        detail.Consignor,
				Consignee:        detail.Consignee,
				ConsigneePhone:   detail.ConsigneePhone,
				CargoRcvUnitName: "",
				CargoRcvAddr:     detail.EndAddr,
				ArrivalTime:      detail.ArrivalTime,
				PaymentTerm:      detail.PaymentTerm,
				StartArea:        detail.StartArea,
				EndArea:          detail.EndArea,
				CarrierOwnerAddr: "",
				DrivingLicneseNo: "",
				NeedOilCard:      detail.NeedOilCard,
			}

			if cro, err := mbrRepo.GetCarrierOwner(detail.CarrierOwnerId); err == nil {
				tc.CarrierOwnerName = cro.Name
				tc.CarrierOwnerPhone = cro.Phone
				tc.DrivingLicneseNo = cro.IDCardNo

				mbrDto, err := mbrRepo.GetMemberInfo(detail.CarrierOwnerId)
				if err == nil {
					if mbrDto.TypeId == 4 {
						tc.VehicleNo = cro.ShipName
					}
				}
			}
		}

		var cargoTypeName string

		startArea := l.GetAreaFullName(tc.StartArea)
		endArea := l.GetAreaFullName(tc.EndArea)

		cargoTypeMap := g.GetDict("106")
		if len(cargoTypeMap) > 0 {
			cargoTypeName = cargoTypeMap[strconv.Itoa(tc.CargoType)]
		}

		totalBillAmount := float64(detail.Deposit + detail.Price)

		var arrivalTime string
		if len(tc.ArrivalTime) >= 10 {
			arrivalTime = tc.ArrivalTime[:10]
		}

		if detail.State > 5 {
			if contract, err = orderRepo.GetContract(int(mId), int(wId), 1); err != nil {
				util.GetLogger().Error("[model-viewTransportContract] - error:%s", err.Error())
			}
		} else {
			var offlinePayInfo data.PayOfflineDto
			if offlinePayInfo, err = payRepo.GetOfflinePayInfo(int(wId)); err == nil {
				invoiceIssueCorp := offlinePayInfo.InvoiceIssueCorp
				if len(invoiceIssueCorp) == 0 {
					invoiceIssueCorp = "姜堰区快来运0001货物运输服务部"
				}

				delegateRemark := ""
				delegateRecvInfo := ""
				if contractType == 0 {
					delegateRemark = fmt.Sprintf("<div>委托<u>%s</u>支付</div>", detail.CargoOwner)
					delegateRecvInfo = fmt.Sprintf("<div>9.运费收款人姓名：<u>%s</u> 。</div><div>10.收款人身份证号：<u>%s</u> 。</div>",
						offlinePayInfo.PayeeName, offlinePayInfo.PayeeIDCard)
				} else {
					delegateRecvInfo = fmt.Sprintf("<div>9.运费收款人姓名：<u>%s</u> 。</div><div>10.收款人身份证号：<u>%s</u> 。</div><div>11.运费收款人开户行：<u>%s</u> 。</div><div>12.收款人银行卡号：<u>%s</u> 。</div>",
						offlinePayInfo.PayeeName, offlinePayInfo.PayeeIDCard, offlinePayInfo.BankName, offlinePayInfo.BankCard)
				}

				contract = fmt.Sprintf(offline_power_of_attorney, waybillId, invoiceIssueCorp, tc.CarrierOwnerName,
					tc.VehicleNo, tc.CarrierOwnerPhone, tc.DrivingLicneseNo, startArea, endArea, tc.CargoName, cargoTypeName, tc.CargoValue,
					totalBillAmount, delegateRemark, tc.Weight, tc.Bulk, tc.CargoCount,
					"", tc.Consignee, tc.ConsigneePhone, tc.CargoRcvAddr, arrivalTime, delegateRecvInfo, invoiceIssueCorp, tc.CarrierOwnerName, detail.OrderTime)
			}

		}
	}
	return
}

func (o Order) ViewLetterOfAuthorization(waybillId ID) (contract string) {
	if len(letter_of_authorization) != 0 {
		var wId int64
		var err error
		var repo data.OrderRepo

		if _, _, _, wId, err = waybillId.Decode(); err != nil {
			err = errors.New(errors.MSG_Mbr_Invalid)
			return
		}

		if dto, err := repo.GetLetterOfAuthorization(int(wId)); err == nil {
			contract = fmt.Sprintf(letter_of_authorization, fmt.Sprintf("IP%s", string(waybillId)), "赵乙林",
				dto.IDCardNo, waybillId, dto.CarrierOwner, dto.IDCardNo, dto.VehicleNo, dto.OrderTime)
		}
	}
	return
}

func (o Order) CompleteOfflineInvoice(memberID, waybillID ID) error {
	var orepo data.OrderRepo

	_, _, _, mid, err := memberID.Decode()
	if err != nil {
		return errors.New(errors.MSG_Mbr_Invalid)
	}

	_, _, _, wid, err := waybillID.Decode()
	if err != nil {
		return errors.New(errors.MSG_Order_Invalid_Waybill_ID)
	}

	wd, err := orepo.GetWaybillDetail(int(wid))
	if err != nil {
		return errors.New(errors.MSG_Order_Invalid_Waybill_ID)
	}

	if wd.State != int(O_Complete) {
		return errors.New(errors.MSG_Order_Invoice_Try_Complete_Error)
	}

	err = orepo.CompleteInvoice(int(mid), int(wid))
	if err != nil {
		util.GetLogger().Error("[CompleteOfflineInvoice] - error:%s", err.Error())
		err = errors.New(errors.MSG_Order_Invoice_Complete_Error)
	}
	return err
}

func (o Order) checkValidWaybillRQ(pubId int, rq *MakeWaybillRQ) (err error) {
	var minDepositAmount, minPriceAmount float64
	var shipmentTime, arrivalTime time.Time
	var pub data.CarrierPubDetail
	var logiRepo data.LogisticsRepo
	var general General
	//var orderRepo data.OrderRepo
	//check cargo type

	// if rq.StartArea%10000 == 0 || rq.EndArea%10000 == 0 {
	// 	err = errors.New(errors.MSG_Order_Invalid_Start_Or_End_Area)
	// 	return
	// }

	if len(rq.CargoName) == 0 {
		err = errors.New(errors.MSG_Order_Invalid_Cargo_Name)
		return
	}

	if minDepositAmount, err = strconv.ParseFloat(general.GetParams("min_deposit_amount"), 32); err == nil {
		if float64(rq.Deposit) < minDepositAmount {
			err = errors.New(fmt.Sprintf("%s%v元", errors.MSG_Order_Deposit_Amount_Not_Less_Than, minDepositAmount))
			return
		}
	}

	if minPriceAmount, err = strconv.ParseFloat(general.GetParams("min_price_amount"), 32); err == nil {
		if float64(rq.Price) < minPriceAmount {
			err = errors.New(fmt.Sprintf("%s%v元", errors.MSG_Order_Price_Amount_Not_Less_Than, minPriceAmount))
			return
		}
	}

	if pub, err = logiRepo.GetCarrierPubDetail(pubId); err == nil {
		rq.CarrierOwnerId = pub.MemberId
		// if pub.SrcCity == 0 {
		// 	if pub.SrcProvince != rq.StartArea-rq.StartArea%10000 {
		// 		err = errors.New(errors.MSG_Order_Invalid_Start_Area)
		// 		return
		// 	}
		// } else {
		// 	if pub.SrcCity != rq.StartArea {
		// 		err = errors.New(errors.MSG_Order_Invalid_Start_Area)
		// 		return
		// 	}
		// }

		// if pub.DestCity == 0 {
		// 	if pub.DestProvince != rq.EndArea-rq.EndArea%10000 {
		// 		err = errors.New(errors.MSG_Order_Invalid_End_Area)
		// 		return
		// 	}
		// } else {
		// 	if pub.DestCity != rq.EndArea {
		// 		err = errors.New(errors.MSG_Order_Invalid_End_Area)
		// 		return
		// 	}
		// }

		if len(rq.ShipmentTime) == 10 {
			if shipmentTime, err = time.Parse("2006-01-02", rq.ShipmentTime); err != nil {
				err = errors.New(errors.MSG_Order_Invalid_Shipment_Time)
				return
			}
		} else if len(rq.ShipmentTime) == 19 {
			if shipmentTime, err = time.Parse("2006-01-02 15:04:05", rq.ShipmentTime); err != nil {
				err = errors.New(errors.MSG_Order_Invalid_Shipment_Time)
				return
			} else {
				rq.ShipmentTime = shipmentTime.Format("2006-01-02 00:00:00")
			}
		} else {
			err = errors.New(errors.MSG_Order_Invalid_Shipment_Time)
		}

		if len(rq.ArrivalTime) != 0 {
			if len(rq.ArrivalTime) == 10 {
				if arrivalTime, err = time.Parse("2006-01-02", rq.ArrivalTime); err != nil {
					err = errors.New(errors.MSG_Order_Invalid_Arrival_Time)
					return
				}
			} else if len(rq.ArrivalTime) == 19 {
				if arrivalTime, err = time.Parse("2006-01-02 15:04:05", rq.ArrivalTime); err != nil {
					err = errors.New(errors.MSG_Order_Invalid_Arrival_Time)
					return
				} else {
					rq.ArrivalTime = arrivalTime.Format("2006-01-02 00:00:00")
				}
			} else {
				err = errors.New(errors.MSG_Order_Invalid_Arrival_Time)
			}
		}
	} else {
		err = errors.New(errors.MSG_Order_Invalid_Carrier_Pub)
	}
	return err
}

func addRewardIfAny(waybillId int) {
	var err error
	var info data.MemberDto
	var waybillDetail data.WaybillDetailDto
	var orderRepo data.OrderRepo
	var mbrRepo data.MemberRepo
	if waybillDetail, err = orderRepo.GetWaybillDetail(waybillId); err == nil {
		if waybillDetail.NeedInvoice {
			if info, err = mbrRepo.GetMemberInfo(waybillDetail.CargoOwnerId); err == nil {
				if info.Inviter != 0 {
					err = AddReward(info.Inviter, waybillId)
				}
			}
		}
	}
	if err != nil {
		util.GetLogger().Info("[model-AddRewayIfAny] - error:%s", err.Error())
	}
}

func autoComplete() {
	var repo data.OrderRepo
	rand.Seed(time.Now().Unix())
	t1 := time.NewTimer(5*time.Minute + time.Duration(rand.Intn(100))*time.Second)
	for {
		select {
		case <-t1.C:
			util.GetLogger().Info("start waybill auto complete")
			if notCompleteWaybills, err := repo.GetNotCompleteWaybills(); err == nil {
				for _, id := range notCompleteWaybills {

					var repo data.OrderRepo
					if detail, err := repo.GetWaybillDetail(id.WaybillId); err == nil {
						if detail.PaymentTerm == 0 {
							util.GetLogger().Info("begin waybill auto complete. id:%d", id.WaybillId)
							if err = topUp(id.WaybillId, 1); err == nil {
								if err = repo.CompleteWaybill(id.WaybillId, id.CargoOwnerId); err != nil {
									util.GetLogger().Error("[model-WaybillAutoComplete] - error:%s", err.Error())
								} else {
									AddPointForOrderCompletion(id.CarrierOwnerId, id.WaybillId)
									AddPointForOrderCompletion(id.CargoOwnerId, id.WaybillId)
									addRewardIfAny(id.WaybillId)
								}
							}
						} else {
							if err = repo.CompleteWaybill(id.WaybillId, id.CargoOwnerId); err != nil {
								util.GetLogger().Error("[model-WaybillAutoComplete] - error:%s", err.Error())
							}
						}
					}
				}
			}
			rand.Seed(time.Now().Unix())
			t1.Reset(5*time.Minute + time.Duration(rand.Intn(100))*time.Second)
		}
	}
}

func saveContract(waybillId int, detail data.WaybillDetailDto, logisticCorp string, contractType int, taxRatio float32, priceIncludeTax float32) {
	var id ID
	var o Order
	var repo data.OrderRepo

	id.Encode(ID_Shard, ID_Ver, ID_Waybill_Category, int64(waybillId))
	rq := &MakeWaybillRQ{
		Id:              id,
		StartArea:       detail.StartArea,
		StartAddr:       detail.StartAddr,
		EndArea:         detail.EndArea,
		EndAddr:         detail.EndAddr,
		ShipmentTime:    detail.ShipmentTime,
		CargoName:       detail.CargoName,
		CargoType:       detail.CargoType,
		CargoBulk:       detail.CargoBulk,
		CargoWeight:     detail.CargoWeight,
		CargoCount:      detail.CargoCount,
		CargoValue:      detail.CargoValue,
		ArrivalTime:     detail.ArrivalTime,
		Deposit:         detail.Deposit,
		Price:           detail.Price,
		Consignor:       detail.Consignor,
		Consignee:       detail.Consignee,
		ConsigneePhone:  detail.ConsigneePhone,
		Remark:          detail.Remark,
		NeedInvoice:     detail.NeedInvoice,
		NeedInsurance:   detail.NeedInsurance,
		PaymentTerm:     detail.PaymentTerm,
		ContractType:    contractType,
		TaxRatio:        taxRatio,
		PriceIncludeTax: priceIncludeTax,
	}
	if len(logisticCorp) != 0 {
		rq.LogisticCorp = logisticCorp
	}
	var cargoOwnerId, carrierOwnerId ID
	cargoOwnerId.Encode(ID_Shard, ID_Ver, ID_Member_Category, int64(detail.CargoOwnerId))
	carrierOwnerId.Encode(ID_Shard, ID_Ver, ID_Member_Category, int64(detail.CarrierOwnerId))

	contract := o.ViewContract(cargoOwnerId, rq)
	if err := repo.SaveContract(data.ContractDto{
		MemberId:  detail.CargoOwnerId,
		WaybillId: waybillId,
		TypeId:    1,
		Content:   contract,
	}); err != nil {
		util.GetLogger().Error("[model-saveContract] - error:%s", err.Error())
	}
	contract = o.ViewContract(carrierOwnerId, rq)
	if err := repo.SaveContract(data.ContractDto{
		MemberId:  detail.CarrierOwnerId,
		WaybillId: waybillId,
		TypeId:    1,
		Content:   contract,
	}); err != nil {
		util.GetLogger().Error("[model-saveContract] - error:%s", err.Error())
	}
	if detail.NeedInvoice {
		var wid ID
		wid.Encode(ID_Shard, ID_Ver, ID_Waybill_Category, int64(waybillId))
		contract = o.ViewLetterOfAuthorization(wid)
		if err := repo.SaveContract(data.ContractDto{
			MemberId:  detail.CarrierOwnerId,
			WaybillId: waybillId,
			TypeId:    2,
			Content:   contract,
		}); err != nil {
			util.GetLogger().Error("[model-saveContract] - error:%s", err.Error())
		}
	}
}
